package com.ruoyi.web.controller.system;

import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.*;
import com.ruoyi.common.core.domain.model.UserAchieveVo;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.enums.DictDataTypeEnums;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.service.*;
import com.train.base.domain.*;
import com.train.base.entity.bo.DailyRecord;
import com.train.base.entity.bo.PostAnalysis;
import com.train.base.entity.bo.SpecialTrainTree;
import com.train.base.enums.ResultTypeEnums;
import com.train.base.service.*;
import org.apache.commons.lang3.ArrayUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;

/**
 * 用户信息
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/user")
public class SysUserController extends BaseController {
    @Autowired
    private ISysUserService userService;
    @Autowired
    private ISysRoleService roleService;
    @Autowired
    private ISysDeptService deptService;
    @Autowired
    private ISysPostService postService;
    @Autowired
    private IExamSubjectKnowledgePointsService examSubjectKnowledgePointsService;
    @Autowired
    private IExamQuestionBankService examQuestionBankService;
    @Autowired
    private ITrainService trainService;
    @Autowired
    private IExamTestPaperService examTestPaperService;
    @Autowired
    private IExamPlanService examPlanService;
    @Autowired
    private IExamPlanVenueService examPlanVenueService;
    @Autowired
    private ISysDictDataService dictDataService;
    @Autowired
    private ISysUserAchieveService iSysUserAchieveService;

    /**
     * 获取用户列表
     */
    @GetMapping("/list")
    public TableDataInfo list(SysUser user) {
        startPage();
        List<SysUser> list = userService.selectUserList(user);
        return getDataTable(list);
    }


    /**
     * 根据岗位id获取该岗位下的用户信息
     */
    @GetMapping("/listByPost")
    public AjaxResult listByPost(Long postId) {
        List<SysUser> list = userService.listByPost(postId);
        if(list.isEmpty()){
            return success(list);
        }
        List<Long> userIds = list.stream().map(SysUser::getUserId).collect(Collectors.toList());
        List<UserAchieveVo> userAchieveVos = iSysUserAchieveService.listAchieveByUserIds(userIds);
        Map<Long,List<UserAchieveVo>> userAchieveVoMap = userAchieveVos.stream().collect(Collectors.groupingBy(UserAchieveVo::getUserId));
        for (SysUser sysUser : list) {
            sysUser.setUserAchieveVos(userAchieveVoMap.get(sysUser.getUserId()));
        }
        return success(list);
    }

    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, SysUser user) {
        List<SysUser> list = userService.selectUserList(user);
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        util.exportExcel(response, list, "用户数据");
    }

    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
    @PostMapping("/importData")
    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        List<SysUser> userList = util.importExcel(file.getInputStream());
        String operName = getUsername();
        String message = userService.importUser(userList, updateSupport, operName);
        return success(message);
    }

    @PostMapping("/importTemplate")
    public void importTemplate(HttpServletResponse response) {
        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
        util.importTemplateExcel(response, "用户数据");
    }

    /**
     * 根据用户编号获取详细信息
     */
    @GetMapping(value = {"/", "/{userId}"})
    public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) {
        userService.checkUserDataScope(userId);
        AjaxResult ajax = AjaxResult.success();
        List<SysRole> roles = roleService.selectRoleAll();
        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
        ajax.put("posts", postService.selectPostAll());
        if (StringUtils.isNotNull(userId)) {
            SysUser sysUser = userService.selectUserById(userId);
            ajax.put(AjaxResult.DATA_TAG, sysUser);
            ajax.put("postIds", postService.selectPostListByUserId(userId));
            ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
            ajax.put("postWithLevel", userService.selectPostWithLevel(userId));
        }
        return ajax;
    }

    /**
     * 根据条件查找用户
     * 部门、岗位、岗位级别
     */
    @GetMapping(value = {"/getByCondition"})
    public AjaxResult getByCondition(@RequestParam(required = false) Long deptId, @RequestParam(required = false) Long postId, @RequestParam(required = false) String postLevelCode) {
        List<SysUser> sysUsers = userService.getByCondition(deptId, postId, postLevelCode);
        return AjaxResult.success(sysUsers);
    }

    /**
     * 新增用户
     */
    @Log(title = "用户管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@Validated @RequestBody SysUser user) {
        if (!userService.checkUserNameUnique(user)) {
            return error("新增用户'" + user.getUserName() + "'失败，登录账号已存在");
        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
            return error("新增用户'" + user.getUserName() + "'失败，手机号码已存在");
        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
            return error("新增用户'" + user.getUserName() + "'失败，邮箱账号已存在");
        }
        user.setCreateBy(getUsername());
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        return toAjax(userService.insertUser(user));
    }

    /**
     * 修改用户
     */
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysUser user) {
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getUserId());
        if (!userService.checkUserNameUnique(user)) {
            return error("修改用户'" + user.getUserName() + "'失败，登录账号已存在");
        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
            return error("修改用户'" + user.getUserName() + "'失败，手机号码已存在");
        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
            return error("修改用户'" + user.getUserName() + "'失败，邮箱账号已存在");
        }
        user.setUpdateBy(getUsername());
        return toAjax(userService.updateUser(user));
    }

    /**
     * 删除用户
     */
    @Log(title = "用户管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{userIds}")
    public AjaxResult remove(@PathVariable Long[] userIds) {
        if (ArrayUtils.contains(userIds, getUserId())) {
            return error("当前用户不能删除");
        }
        return toAjax(userService.deleteUserByIds(userIds));
    }

    /**
     * 重置密码
     */
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/resetPwd")
    public AjaxResult resetPwd(@RequestBody SysUser user) {
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getUserId());
        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
        user.setUpdateBy(getUsername());
        return toAjax(userService.resetPwd(user));
    }

    /**
     * 状态修改
     */
    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
    @PutMapping("/changeStatus")
    public AjaxResult changeStatus(@RequestBody SysUser user) {
        userService.checkUserAllowed(user);
        userService.checkUserDataScope(user.getUserId());
        user.setUpdateBy(getUsername());
        return toAjax(userService.updateUserStatus(user));
    }

    /**
     * 根据用户编号获取授权角色
     */
    @GetMapping("/authRole/{userId}")
    public AjaxResult authRole(@PathVariable("userId") Long userId) {
        AjaxResult ajax = AjaxResult.success();
        SysUser user = userService.selectUserById(userId);
        List<SysRole> roles = roleService.selectRolesByUserId(userId);
        ajax.put("user", user);
        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
        return ajax;
    }

    /**
     * 用户授权角色
     */
    @Log(title = "用户管理", businessType = BusinessType.GRANT)
    @PutMapping("/authRole")
    public AjaxResult insertAuthRole(Long userId, Long[] roleIds) {
        userService.checkUserDataScope(userId);
        userService.insertUserAuth(userId, roleIds);
        return success();
    }

    /**
     * 获取部门树列表
     */
    @GetMapping("/deptTree")
    public AjaxResult deptTree(SysDept dept) {
        return success(deptService.selectDeptTreeList(dept));
    }


    /**
     * 获取某个用户的岗位详情
     *
     * @return
     */
    @GetMapping(value = "getPostDetail")
    public AjaxResult getPostDetail() {
        return success(userService.getPostDetail());
    }

    /**
     * 获取用户的岗位分析
     *
     * @return
     */
    @GetMapping(value = "getPostAnalysis")
    public AjaxResult getPostAnalysis() {
        List<SysUserPost> sysUserPosts = userService.selectAll();
        Map<Long, Set<Long>> postMap = sysUserPosts.stream().collect(Collectors.groupingBy(SysUserPost::getPostId, Collectors.mapping(SysUserPost::getUserId, toSet())));
        Map<String, Set<Long>> postLevelMap = sysUserPosts.stream().collect(Collectors.groupingBy(SysUserPost::getPostLevelCode, Collectors.mapping(SysUserPost::getUserId, toSet())));
        Map<String, Set<Long>> postAbilitieMap = sysUserPosts.stream().collect(Collectors.groupingBy(SysUserPost::getPostAbility, Collectors.mapping(SysUserPost::getUserId, toSet())));
        PostAnalysis postAnalysis = new PostAnalysis();
        List<PostAnalysis.Post> posts = new ArrayList<>();
        List<PostAnalysis.PostLevel> postLevels = new ArrayList<>();
        List<PostAnalysis.PostAbility> postAbilities = new ArrayList<>();
        List<SysPost> sysPosts = postService.selectPostAll();
        Map<Long, String> sysPostMap = sysPosts.stream().collect(toMap(SysPost::getPostId, SysPost::getPostName));
        postMap.forEach((k, v) -> {
            PostAnalysis.Post post = new PostAnalysis.Post();
            post.setName(sysPostMap.get(k));
            post.setValue(v.size());
            posts.add(post);
        });
        SysDictData postLevelData = new SysDictData();
        postLevelData.setDictType(DictDataTypeEnums.POST_LEVEL.getCode());
        List<SysDictData> levelDicts = dictDataService.selectDictDataList(postLevelData);
        Map<String, String> levelMap = levelDicts.stream().collect(toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
        postLevelMap.forEach((k, v) -> {
            PostAnalysis.PostLevel postLevel = new PostAnalysis.PostLevel();
            postLevel.setName(levelMap.get(k));
            postLevel.setValue(v.size());
            postLevels.add(postLevel);
        });
        SysDictData postAbilitieData = new SysDictData();
        postAbilitieData.setDictType(DictDataTypeEnums.POST_ABILITY.getCode());
        List<SysDictData> abilityDicts = dictDataService.selectDictDataList(postAbilitieData);
        Map<String, String> abilityMap = abilityDicts.stream().collect(toMap(SysDictData::getDictValue, SysDictData::getDictLabel));
        postAbilitieMap.forEach((k, v) -> {
            PostAnalysis.PostAbility postAbility = new PostAnalysis.PostAbility();
            postAbility.setName(abilityMap.get(k));
            postAbility.setValue(v.size());
            postAbilities.add(postAbility);
        });
        postAnalysis.setPosts(posts);
        postAnalysis.setPostLevels(postLevels);
        postAnalysis.setPostAbilities(postAbilities);
        return success(postAnalysis);
    }

    /**
     * 获取某个月的日常
     * 1.每天专项训练了多少题目
     * 2.相关的考试计划信息（考卷信息）
     * 3.相关的专项模拟数据
     *
     * @param month
     * @return
     */
    @GetMapping(value = "listDaily")
    public AjaxResult listDaily(@RequestParam LocalDate month) {
        // 获取专项训练数量
        List<TrainRecord> specialTrainRecords = trainService.selectSpecialRecordByDate(month);
        Map<LocalDate, List<TrainRecord>> specialTrainRecordMap = specialTrainRecords.stream().collect(Collectors.groupingBy(record -> record.getCreateTime().toLocalDate()));
        // 获取专项模拟数据
        List<TrainTestPaper> trainTestPapers = trainService.listUserTrainPaperByDate(month);
        Map<LocalDate, List<TrainTestPaper>> trainTestPaperMap = trainTestPapers.stream().collect(Collectors.groupingBy(new Function<TrainTestPaper, LocalDate>() {
            @Override
            public LocalDate apply(TrainTestPaper trainTestPaper) {
                return trainTestPaper.getCreateTime().toLocalDate();
            }
        }));
        // 获取考试信息
        List<ExamTestPaper> examTestPapers = examTestPaperService.listByDate(month);
        Map<LocalDate, List<ExamTestPaper>> examTestPaperMap = examTestPapers.stream().collect(Collectors.groupingBy(new Function<ExamTestPaper, LocalDate>() {
            @Override
            public LocalDate apply(ExamTestPaper examTestPaper) {
                return examTestPaper.getCreateTime().toLocalDate();
            }
        }));
        // 获取考试计划信息
        List<ExamPlan> examPlans = examPlanService.listByDate(month);
        Map<LocalDate, List<ExamPlan>> examPlanMap = examPlans.stream().collect(Collectors.groupingBy(examPlan -> examPlan.getExamStartTime().toLocalDate()));
        List<Long> planIds = examPlans.stream().map(ExamPlan::getId).toList();
        List<ExamPlanVenue> venues = examPlanVenueService.selectExamPlanVenueByPlanIds(planIds);
        Map<Long, List<ExamPlanVenue>> venueMap = venues.stream().collect(Collectors.groupingBy(ExamPlanVenue::getExamPlanId));
        List<DailyRecord> dailyRecords = new ArrayList<>();
        for (int i = 1; i <= month.lengthOfMonth(); i++) {
            DailyRecord record = new DailyRecord();
            DailyRecord.SpecialTrainRecord specialTrainRecord = new DailyRecord.SpecialTrainRecord();
            LocalDate date = LocalDate.of(month.getYear(), month.getMonthValue(), i);
            record.setDate(date);
            List<TrainRecord> records = specialTrainRecordMap.get(date);
            if (!CollectionUtils.isEmpty(records)) {
                specialTrainRecord.setRecords(records);
                int rightNum = 0;
                for (TrainRecord trainRecord : records) {
                    if (Objects.equals(ResultTypeEnums.RIGHT.getCode(), trainRecord.getResult())) {
                        rightNum++;
                    }
                }
                specialTrainRecord.setNum(records.size());
                specialTrainRecord.setRightNum(rightNum);
            }
            record.setSpecialTrainRecord(specialTrainRecord);

            DailyRecord.SimulationTrainRecord simulationTrainRecord = new DailyRecord.SimulationTrainRecord();
            simulationTrainRecord.setPapers(trainTestPaperMap.get(date));
            record.setSimulationTrainRecord(simulationTrainRecord);

            DailyRecord.ExamRecord examRecord = new DailyRecord.ExamRecord();
            examRecord.setPapers(examTestPaperMap.get(date));
            record.setExamRecord(examRecord);

            DailyRecord.ExamPlanRecord examPlanRecord = new DailyRecord.ExamPlanRecord();
            List<DailyRecord.ExamPlanRecord.PlanDetail> planDetails = new ArrayList<>();
            List<ExamPlan> plans = examPlanMap.get(date);
            if (!CollectionUtils.isEmpty(plans)) {
                plans.forEach(p -> {
                    DailyRecord.ExamPlanRecord.PlanDetail detail = new DailyRecord.ExamPlanRecord.PlanDetail();
                    detail.setExamPlan(p);
                    for (ExamPlanVenue examPlanVenue : venueMap.get(p.getId())) {
                        boolean flag = false;
                        for (ExamPlanVenueUser examPlanVenueUser : examPlanVenue.getExamPlanVenueUserList()) {
                            if (examPlanVenueUser.getUserId().equals(SecurityUtils.getUserId())) {
                                detail.setVenue(examPlanVenue);
                                flag = true;
                                break;
                            }
                        }
                        if (flag) {
                            break;
                        }
                    }
                    planDetails.add(detail);
                });
            }
            examPlanRecord.setDetails(planDetails);
            record.setExamPlanRecord(examPlanRecord);
            dailyRecords.add(record);
        }
        return success(dailyRecords);
    }

    /**
     * 获取用户可访问的岗位-科目-知识点树状结构
     *
     * @return
     */
    @GetMapping("/getPostSubjectTree")
    public AjaxResult getPostSubjectTree() {
        // 获取用户可访问的岗位信息
        List<SysUserPost> sysUserPosts = userService.selectPostWithLevel(SecurityUtils.getUserId());
        if (sysUserPosts.isEmpty()) {
            return AjaxResult.error("请配置用户岗位");
        }
        // 用户可方位的岗位信息id集合
        List<Long> postIds = sysUserPosts.stream().map(SysUserPost::getPostId).collect(toSet()).stream().toList();
        List<SysPost> sysPosts = postService.selectPostListByIds(postIds);
        Map<Long, String> postMap = sysPosts.stream().collect(Collectors.toMap(SysPost::getPostId, SysPost::getPostName));
        // 根据岗位id获取科目信息
        List<ExamSubjectKnowledgePoints> subjects = examSubjectKnowledgePointsService.selectByPostIds(postIds);
        if (subjects.isEmpty()) {
            return AjaxResult.error("请维护科目信息");
        }
        Map<Long, List<ExamSubjectKnowledgePoints>> postSubjectMap = subjects.stream().collect(Collectors.groupingBy(ExamSubjectKnowledgePoints::getPostId));
        // 根据科目信息获取知识点信息
        List<ExamSubjectKnowledgePoints> knowledgePoints = examSubjectKnowledgePointsService.selectBySubjectIds(subjects.stream().map(ExamSubjectKnowledgePoints::getId).collect(Collectors.toList()));
        if (knowledgePoints.isEmpty()) {
            return AjaxResult.error("请维护知识点信息");
        }
        Map<Long, List<ExamSubjectKnowledgePoints>> subjectknowledgePointMap = knowledgePoints.stream().collect(Collectors.groupingBy(ExamSubjectKnowledgePoints::getSubjectId));
        List<SpecialTrainTree.PostSubjectNode> postSubjectNodes = new ArrayList<>();
        sysUserPosts.forEach(e -> {
            SpecialTrainTree.PostSubjectNode postSubjectNode = new SpecialTrainTree.PostSubjectNode();
            postSubjectNode.setId(e.getPostId());
            postSubjectNode.setName(postMap.get(e.getPostId()));
            postSubjectNode.setPostLevelCode(e.getPostLevelCode());
            List<SpecialTrainTree.SubjectKnowledgePointNode> subjectKnowledgePointNodes = new ArrayList<>();
            if (CollectionUtils.isEmpty(postSubjectMap.get(e.getPostId()))) {
                return;
            }
            for (ExamSubjectKnowledgePoints subject : postSubjectMap.get(e.getPostId())) {
                SpecialTrainTree.SubjectKnowledgePointNode subjectKnowledgePointNode = new SpecialTrainTree.SubjectKnowledgePointNode();
                subjectKnowledgePointNode.setId(subject.getId());
                subjectKnowledgePointNode.setName(subject.getName());
                List<SpecialTrainTree.KnowledgePointNode> knowledgePointNodes = new ArrayList<>();
                if (CollectionUtils.isEmpty(subjectknowledgePointMap.get(subject.getId()))) {
                    continue;
                }
                for (ExamSubjectKnowledgePoints point : subjectknowledgePointMap.get(subject.getId())) {
                    SpecialTrainTree.KnowledgePointNode node = new SpecialTrainTree.KnowledgePointNode();
                    node.setId(point.getId());
                    node.setName(point.getName());
                    knowledgePointNodes.add(node);
                    // 针对岗位，岗位级别，科目，知识点进行进度信息配置
                    ExamQuestionBank bank = new ExamQuestionBank();
                    bank.setPostId(e.getPostId());
                    bank.setSubjectId(subject.getId());
                    bank.setKnowledgePointsId(point.getId());
                    List<ExamQuestionBank> examQuestionBanks = examQuestionBankService.selectExamQuestionBankList(bank);
                    if (examQuestionBanks.isEmpty()) {
                        continue;
                    }
                    List<TrainRecord> trainRecords = trainService.selectSpecialRecord(SecurityUtils.getUserId(), examQuestionBanks.stream().map(ExamQuestionBank::getId).collect(Collectors.toList()));
                    List<SpecialTrainTree.TrainScheduleNode> trainScheduleNodes = new ArrayList<>();
                    for (Integer i = 0; i <= Integer.valueOf(e.getPostLevelCode()); i++) {
                        SpecialTrainTree.TrainScheduleNode trainScheduleNode = new SpecialTrainTree.TrainScheduleNode();
                        String level = String.valueOf(i);
                        Set<ExamQuestionBank> banks = examQuestionBanks.stream().filter(b -> b.getPostLevelCode().equals(level)).collect(toSet());
                        if (banks.isEmpty()) {
                            continue;
                        }
                        List<Long> bankIds = banks.stream().map(ExamQuestionBank::getId).collect(Collectors.toList());
                        trainScheduleNode.setPostLevelId(level);
                        trainScheduleNode.setQuestionNum(banks.size());
                        int trainNum = 0, rightNum = 0, errorNum = 0;
                        for (TrainRecord trainRecord : trainRecords) {
                            if (!trainRecord.getPostLevelCode().equals(String.valueOf(i))) {
                                continue;
                            }
                            trainNum++;
                            if (bankIds.contains(trainRecord.getQuestionId())) {
                                if (ResultTypeEnums.ERROR.getCode().equals(trainRecord.getResult())) {
                                    errorNum++;
                                } else if (ResultTypeEnums.RIGHT.getCode().equals(trainRecord.getResult())) {
                                    rightNum++;
                                }
                            }
                        }
                        trainScheduleNode.setTrainNum(trainNum);
                        trainScheduleNode.setRightNum(rightNum);
                        trainScheduleNode.setErrorNum(errorNum);
                        trainScheduleNodes.add(trainScheduleNode);
                    }
                    node.setTrainSchedules(trainScheduleNodes);
                }
                subjectKnowledgePointNode.setChildren(knowledgePointNodes);
                subjectKnowledgePointNodes.add(subjectKnowledgePointNode);
            }
            postSubjectNode.setChildren(subjectKnowledgePointNodes);
            postSubjectNodes.add(postSubjectNode);
        });
        return AjaxResult.success(postSubjectNodes);
    }

    /**
     * 查询用户的勋章信息
     */
    @GetMapping("listAchieveByUserId")
    public AjaxResult listAchieveByUserId() {
        // 查询用户的勋章
        AjaxResult ajax = AjaxResult.success();
        ajax.put("userAchieveVo", iSysUserAchieveService.listAchieveByUserId(SecurityUtils.getUserId()));
        return ajax;
    }
}
